'use strict'

const http=require('http');//动态导入，允许大小写
const fs=require('fs');

const fsp=fs.promises
/**————————————————————解析Body数据*—————————————————————————*/
/**
 * Content-Disposition:form-data;name="file";filename="xxx"
 * Content-Type:text/plain;
 * 
 * @param {string} headerData 
 */
function parseBodyHeaders(headerData){
    let hlist=headerData.split('\r\n').filter(p=>p.length>0);

    //解析content-disposition=>hlist[0]
    //按照; 切分数组，选出从第二个元素开始的数组
    let csplit=hlist[0].split('; ').slice(1);

    let filename
    let name
    for(let a of csplit){
        if(a.indexOf('filename=')===0){
            filename=a.substring(a.indexOf('=')+2,a.length-1);//filename='xxx',从x开始
        }else{
            name=a.substring(a.indexOf('=')+2,a.length-1);
        }
    }

    let headers={}
    let ind=0
    let k
    for(let h of hlist){
        ind=h.indexOf(':')
        k=h.substring(0,ind).toLowerCase()
        headers[k]=h.substring(ind+1).trim()
    }

    return {
        filename,
        name, 
        headers, 
        contentType:headers['content-type'] || ''
    }

}
function parseBody(bodyData,headers) {
    let ctype=headers['content-type']

    let bdy=ctype.substring(ctype.indexOf('=')+1);
    
    let crlf_bdy=`\r\n--${bdy}`;

    let header_end_index=bodyData.indexOf('\r\n\r\n',crlf_bdy.length);

    let headerData=bodyData.toString('utf8',crlf_bdy.length,header_end_index);

    //解析文件头信息
    let hd= parseBodyHeaders(headerData);

    let file={
        start:header_end_index + 4,
        end:bodyData.indexOf(crlf_bdy,header_end_index),
        ...hd
    };
    
    file.length=file.end - file.start;
    return file
}

//解析文件扩展名
function parseExtName(filename){
    if(filename.length<2){
        return ''
    }
    let nsplit=filename.split('.').filter(p=>p.length>0);
    if(nsplit.length<2) return ''

    return `.${nsplit[nsplit.length-1]}`

}

/**——————————————————解析Body -END数据*——————————————————————*/
let html=`
<!DOCTYPE html>
<html>
<head>
    <title>first-http</title>
</head>
<body>
    hello world!
    你好！
</body>
</html>
`

let routerTable={

    GET:{
        '/':async(ctx)=>{
            ctx.response.setHeader('Content-Type', 'text/html;charset=utf-8');
            ctx.end(html)
        },
        '/test':async(ctx)=>{
            let status=parseInt(Math.random()*200)+199;
            ctx.response.statusCode=status;
            ctx.end('test end');
        },
        '/upload':async(ctx)=>{
            let stm=fs.createReadStream('./upload.html');
            // stm.pipe(response);//第二个参数默认true
            stm.pipe(ctx.response,{end:false});
            stm.on('end',()=>{
                ctx.end();
            });
        }
    },

    POST:{
        '/data':async(ctx)=>{
            let file=parseBody(ctx.rawBody,ctx.headers);

            console.log(file);

            if(!ctx.rawBody){
                ctx.end();
                return;
            }

        
            let fname=`${Date.now()}-${Math.random()} `+ `${parseExtName(file.filename)}`;

            let fh=await fsp.open(fname,'w+');//a+追加  w+从头写
            let fret=await fh.write(ctx.rawBody,file.start,file.length,0);

            //关闭打开的文件
            fh.close();
            ctx.end(`${fret.bytesWritten}`);
            // ctx.end(fret.bytesWritten);

            // ctx.end(ctx.rawBody)
         
        }
    }
}

http.createServer(async(request,response)=>{
    let rm=routerTable[request.method];
    
    let usplit=request.url.split('?');

    let pathname=usplit[0];
    
    let querystring=usplit[1] || '';//如果upslit[0]没有，[1]就是undefined，赋值为空
    
    if(!rm || !rm[ pathname ]){//request.url
        response.statusCode=404;
        response.end('page not found');
        return
    }

    //ctx是context的缩写，意味请求上下文，就是一个打包了各种数据和方法的对象
    let ctx={
        method:request.method,
        path:pathname,
        search:querystring,
        headers:request.headers,
        request:request,
        response:response,
        end:(data,encoding='utf8')=>{
            response.end(data,encoding)
        },
        write:(data,encoding='utf8')=>{
            response.write(data,encoding)
        },
        totalLength:0, 
        rawBody:null, 
        bufferList:[],
    }

    await new Promise((rv,rj)=>{
        if('PD'.indexOf(request.method[0])>=0){
            request.on('data',chunk=>{
                ctx.bufferList.push(chunk);
                ctx.totalLength+=chunk.length;
            })
        }else{
            request.on('data',data=>{
                response.statusCode=400;
                response.end('bad request',()=>{
                    request.destroy()//触发close事件不会触发end事件
                })
            })
        }

        let resolved=false;

        request.on('end',()=>{
            resolved=true;
            rv();
        })

        request.on('close',()=>{
            (!resolved) && rv();
        })
    })

    if(!response.writable||response.writableEnded){
        return 
    }

    if(ctx.bufferList.length>0){
        ctx.rawBody=Buffer.concat(ctx.bufferList,ctx.totalLength);
        ctx.bufferList=[];
    }
    //不需要发送请求数据：get、head、trace、options
    //必须发送请求数据：post、put、patch
    rm[pathname](ctx)

    
}).listen(3456)